/*
 * friso dynamaic Array interface implementation defined in header file "friso_API.h".
 *
 * @author  lionsoul<chenxin619315@gmail.com>
 */

#include "friso_API.h"
#include <stdlib.h>

/* ********************************************
 * friso array list static functions block    *
 **********************************************/
__STATIC_API__ void **create_array_entries(uint_t __blocks) {
    register uint_t t;
    void **block = (void **) FRISO_CALLOC(sizeof(void *), __blocks);
    if(block == NULL) {
        ___ALLOCATION_ERROR___
    }

    //initialize
    for(t = 0; t < __blocks; t++) {
        block[t] = NULL;
    }

    return block;
}

//resize the array. (the opacity should not be smaller than array->length)
__STATIC_API__ friso_array_t resize_array_list(
    friso_array_t array,
    uint_t opacity) {
    register uint_t t;
    void **block = create_array_entries(opacity);

    for(t = 0; t < array->length ; t++) {
        block[t] = array->items[t];
    }

    FRISO_FREE(array->items);
    array->items = block;
    array->allocs = opacity;

    return array;
}


/* ********************************************
 * friso array list FRISO_API functions block    *
 **********************************************/
//create a new array list. (A macro define has replace this.)
//FRISO_API friso_array_t new_array_list( void ) {
//    return new_array_list_with_opacity( __DEFAULT_ARRAY_LIST_OPACITY__ );
//}

//create a new array list with a given opacity.
FRISO_API friso_array_t new_array_list_with_opacity(uint_t opacity) {
    friso_array_t array = (friso_array_t)
                          FRISO_MALLOC(sizeof(friso_array_entry));
    if(array == NULL) {
        ___ALLOCATION_ERROR___
    }

    //initialize
    array->items  = create_array_entries(opacity);
    array->allocs = opacity;
    array->length = 0;

    return array;
}

/*
 * free the given friso array.
 *    and its items, but never where its items item pointed to .
 */
FRISO_API void free_array_list(friso_array_t array) {
    //free the allocation that all the items pointed to
    //register int t;
    //if ( flag == 1 ) {
    //    for ( t = 0; t < array->length; t++ ) {
    //        if ( array->items[t] == NULL ) continue;
    //        FRISO_FREE( array->items[t] );
    //        array->items[t] = NULL;
    //    }
    //}

    FRISO_FREE(array->items);
    FRISO_FREE(array);
}

//add a new item to the array.
FRISO_API void array_list_add(friso_array_t array, void *value) {
    //check the condition to resize.
    if(array->length == array->allocs) {
        resize_array_list(array, array->length * 2 + 1);
    }
    array->items[array->length++] = value;
}

//insert a new item at a specified position.
FRISO_API void array_list_insert(
    friso_array_t array,
    uint_t idx,
    void *value) {
    register uint_t t;

    if(idx <= array->length) {
        //check the condition to resize the array.
        if(array->length == array->allocs) {
            resize_array_list(array, array->length * 2 + 1);
        }

        //move the elements after idx.
        //for ( t = idx; t < array->length; t++ ) {
        //    array->items[t+1] = array->items[t];
        //}
        for(t = array->length - 1; t >= idx; t--) {
            array->items[t + 1] = array->items[t];
        }

        array->items[idx] = value;
        array->length++;
    }
}

//get the item at a specified position.
FRISO_API void *array_list_get(friso_array_t array, uint_t idx) {
    if(idx < array->length) {
        return array->items[idx];
    }
    return NULL;
}

//set the value of the item at a specified position.
//this will return the old value.
FRISO_API void * array_list_set(
    friso_array_t array,
    uint_t idx,
    void * value) {
    void * oval = NULL;
    if(idx < array->length) {
        oval = array->items[idx];
        array->items[idx] = value;
    }
    return oval;
}

//remove the item at a specified position.
//this will return the value of the removed item.
FRISO_API void * array_list_remove(
    friso_array_t array, uint_t idx) {
    register uint_t t;
    void *oval = NULL;

    if(idx < array->length) {
        oval = array->items[idx];
        //move the elements after idx.
        for(t = idx; t < array->length - 1; t++) {
            array->items[t] = array->items[ t + 1 ];
        }
        array->items[array->length - 1] = NULL;
        array->length--;
    }

    return oval;
}

/*trim the array list*/
FRISO_API friso_array_t array_list_trim(friso_array_t array) {
    if(array->length < array->allocs) {
        return resize_array_list(array, array->length);
    }
    return array;
}

/*
 * clear the array list.
 *     this function will free all the allocations that the pointer pointed.
 *        but will not free the point array allocations,
 *        and will reset the length of it.
 */
FRISO_API friso_array_t array_list_clear(friso_array_t array) {
    register uint_t t;
    //free all the allocations that the array->length's pointer pointed.
    for(t = 0; t < array->length; t++) {
        /*if ( array->items[t] == NULL ) continue;
          FRISO_FREE( array->items[t] ); */
        array->items[t] = NULL;
    }
    //attribute reset.
    array->length = 0;

    return array;
}

//get the size of the array list. (A macro define has replace this.)
//FRISO_API uint_t array_list_size( friso_array_t array ) {
//    return array->length;
//}

//return the allocations of the array list.(A macro define has replace this)
//FRISO_API uint_t array_list_allocs( friso_array_t array ) {
//    return array->allocs;
//}

//check if the array is empty.(A macro define has replace this.)
//FRISO_API int array_list_empty( friso_array_t array )
//{
//    return ( array->length == 0 );
//}
